<?php
/**
 * 模板解析
 * @author Yangdong Zhang <zhangyangdong@gouuse.cn>
 * @time 2018/10/12 15:48
 */
namespace App\Util;

class Template
{
    /**
     * Template constructor.
     */
    private function __construct()
    {

    }

    /**
     * @return Template
     */
    public static function instance()
    {
        return new self();
    }

    /**
     * 模板函数
     * @param string $template
     * @param string $dir
     * @return string
     */
    public function template($template = 'index', $dir = '')
    {
        $file = RUNTIME . '/cache/tpl/' . ($dir ? $dir . '/' : '') . $template . '.php';

        $tplFile = RUNTIME . '/cache/tpl/';
        if ($dir) $tplFile = $tplFile.$dir;
        if (!is_dir($tplFile)) {
            @mkdir($tplFile, 0777);
        }

        if ($dir) $dir = $dir . '/';
        $from = TEMPLATE_PATH .'/'. $dir . $template . '.html';
        if (!is_file($file) || filemtime($from) > filemtime($file) || (filesize($file) < 52 && filesize($from) > 0)) {
            $this->templateCompile($from, $file);
        }
        return $file;
    }

    private function templateCompile($from, $to)
    {
        $content = $this->templateParse(@file_get_contents($from));
        @file_put_contents($to, $content);
    }


    private function templateParse($str)
    {
        $str = preg_replace("/\<\!\-\-\[(.+?)\]\-\-\>/", "", $str);
        $str = preg_replace("/\<\!\-\-\{(.+?)\}\-\-\>/s", "{\\1}", $str);
        $str = preg_replace("/\{template\s+([^\}]+)\}/", "<?php app template(\\1);?>", $str);
        $str = preg_replace("/\{php\s+(.+)\}/", "<?php \\1?>", $str);
        $str = preg_replace("/\{if\s+(.+?)\}/", "<?php if(\\1) { ?>", $str);
        $str = preg_replace("/\{else\}/", "<?php } else { ?>", $str);
        $str = preg_replace("/\{elseif\s+(.+?)\}/", "<?php } else if(\\1) { ?>", $str);
        $str = preg_replace("/\{\/if\}/", "<?php } ?>\r\n", $str);
        $str = preg_replace("/\{loop\s+(\S+)\s+(\S+)\}/", "<?php if(is_array(\\1)) { foreach(\\1 as \\2) { ?>", $str);
        $str = preg_replace("/\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}/", "<?php if(is_array(\\1)) { foreach(\\1 as \\2 => \\3) { ?>", $str);
        $str = preg_replace("/\{\/loop\}/", "<?php } } ?>", $str);
        $str = preg_replace("/\{([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\(([^{}]*)\))\}/", "<?php echo \\1;?>", $str);
        $str = preg_replace_callback("/<\?php([^\?]+)\?>/s", [$this, 'templateAddQuote1'], $str);
        $str = preg_replace("/\{(\\$[a-zA-Z_\x7f-\xff][\->a-zA-Z0-9_\+\-\x7f-\xff]*)\}/", "<?php echo \\1;?>", $str);
        $str = preg_replace_callback("/\{(\\$[a-zA-Z0-9_\[\]\'\"\$\+\-\x7f-\xff]+)\}/s", [$this, 'templateAddQuote2'], $str);
        $str = preg_replace("/\{([A-Z_\x7f-\xff][A-Z0-9_\x7f-\xff]*)\}/s", "<?php echo \\1;?>", $str);
        $str = preg_replace("/\'([A-Za-z]+)\[\'([A-Za-z\.]+)\'\](.?)\'/s", "'\\1[\\2]\\3'", $str);
        $str = preg_replace("/(\r?\n)\\1+/", "\\1", $str);
        $str = str_replace("\t", '', $str);
        $str = "<?php defined('ROOT_PATH') or exit('Access Denied');?>\n" . trim($str);
        return $str;
    }

    private function templateAddQuote1($matches)
    {
        return str_replace("\\\"", "\"", preg_replace("/\[([a-zA-Z0-9_\-\.\x7f-\xff]+)\]/s", "['\\1']", $matches[0]));
    }

    private function templateAddQuote2($matches)
    {
        return '<?php echo ' . str_replace("\\\"", "\"", preg_replace("/\[([a-zA-Z0-9_\-\.\x7f-\xff]+)\]/s", "['\\1']", $matches[1])) . ';?>';
    }


    //安全函数
    private function dsafe($string, $type = 1)
    {
        if (is_array($string)) {
            return array_map([$this, 'dsafe'], $string);
        } else {
            if ($type) {
                $string = str_replace('<em></em>', '', $string);
                $string = preg_replace("/\<\!\-\-([\s\S]*?)\-\-\>/", "", $string);
                $string = preg_replace("/\/\*([\s\S]*?)\*\//", "", $string);
                $string = preg_replace("/&#([a-z0-9]{1,})/i", "<em></em>&#\\1", $string);
                $match = array("/s[\s]*c[\s]*r[\s]*i[\s]*p[\s]*t/i", "/d[\s]*a[\s]*t[\s]*a[\s]*\:/i", "/b[\s]*a[\s]*s[\s]*e/i", "/e[\\\]*x[\\\]*p[\\\]*r[\\\]*e[\\\]*s[\\\]*s[\\\]*i[\\\]*o[\\\]*n/i", "/i[\\\]*m[\\\]*p[\\\]*o[\\\]*r[\\\]*t/i", "/on([a-z]{2,})([\(|\=|\s]+)/i", "/about/i", "/frame/i", "/link/i", "/meta/i", "/textarea/i", "/eval/i", "/alert/i", "/confirm/i", "/prompt/i", "/cookie/i", "/document/i", "/newline/i", "/colon/i", "/<style/i", "/\\\x/i");
                $replace = array("s<em></em>cript", "da<em></em>ta:", "ba<em></em>se", "ex<em></em>pression", "im<em></em>port", "o<em></em>n\\1\\2", "a<em></em>bout", "f<em></em>rame", "l<em></em>ink", "me<em></em>ta", "text<em></em>area", "e<em></em>val", "a<em></em>lert", "/con<em></em>firm/i", "prom<em></em>pt", "coo<em></em>kie", "docu<em></em>ment", "new<em></em>line", "co<em></em>lon", "<sty1e", "\<em></em>x");
                return str_replace(array('isShowa<em></em>bout', 'co<em></em>ntrols'), array('isShowAbout', 'controls'), preg_replace($match, $replace, $string));
            } else {
                return str_replace(array('<em></em>', '<sty1e'), array('', '<style'), $string);
            }
        }
    }

    private function stripSql($string, $type = 1)
    {
        if (is_array($string)) {
            return array_map([$this, 'stripSql'], $string);
        } else {
            if ($type) {
                $string = preg_replace("/\/\*([\s\S]*?)\*\//", "", $string);
                $string = preg_replace("/0x([a-f0-9]{2,})/i", '0&#120;\\1', $string);
                $string = preg_replace_callback("/(select|update|replace|delete|drop)([\s\S]*?)(from)/i", [$this, 'stripWd'], $string);
                $string = preg_replace_callback("/(load_file|substring|substr|reverse|trim|space|left|right|mid|lpad|concat|concat_ws|make_set|ascii|bin|oct|hex|ord|char|conv)([^a-z]?)\(/i", [$this, 'stripWd'], $string);
                $string = preg_replace_callback("/(union|where|having|outfile|dumpfile)/i", [$this, 'stripWd'], $string);
                return $string;
            } else {
                return str_replace(array('&#95;', '&#100;', '&#101;', '&#103;', '&#105;', '&#109;', '&#110;', '&#112;', '&#114;', '&#115;', '&#116;', '&#118;', '&#120;'), array('_', 'd', 'e', 'g', 'i', 'm', 'n', 'p', 'r', 's', 't', 'v', 'x'), $string);
            }
        }
    }

    private function stripWd($m)
    {
        if (is_array($m) && isset($m[1])) {
            $wd = substr($m[1], 0, -1) . '&#' . ord(strtolower(substr($m[1], -1))) . ';';
            if (isset($m[3])) return $wd . $m[2] . $m[3];
            if (isset($m[2])) return $wd . $m[2] . '(';
            return $wd;
        }
        return '';
    }
}